package zzk.activiti.实战;


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.bpmn.model.Process;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 流程模块
 */
@Controller
@RequestMapping("process")
public class 流程1_流程模块 {
    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 1.部署流程
     */
    @RequestMapping("createProcesses")
    @ResponseBody
    public void createProcesses() {
        //使用获取RepositoryService进行部署
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("static/bpmn/zzkBak.bpmn")//添加bpmn资源
                .addClasspathResource("static/bpmn/zzkBak.png")//添加png资源
                .name("员工请假审批流程")
                .deploy();//部署流程
        //输出流程部署的信息
        System.out.println("流程部署id：" + deployment.getId());
        System.out.println("流程部署名称：" + deployment.getName());
    }

    /**
     * 2.删除流程
     */
    @RequestMapping("deleteProcessesByDeploymentId")
    @ResponseBody
    public void deleteProcessesByDeploymentId() {
        String id = "93edb68e-becc-11ef-9d71-04ed33b13d42";
        //设置true，择优级联删除的效果。false如果已有实例，则会删除错误
        repositoryService.deleteDeployment(id, true);

    }

    /**
     * 3.获取流程列表
     */
    @RequestMapping("getProcessesList")
    @ResponseBody
    public void getProcessesList() {
        // 创建一个查询对象来查询流程定义，并应用模糊查询条件
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery()
//                .processDefinitionId("myProcess_1:14:d9012a96-becd-11ef-a7ae-04ed33b13d42")  // 根据流程id精确查询
//                .processDefinitionNameLike("%员工请假审批流程%")  // 根据流程名称模糊查询
                .orderByProcessDefinitionVersion().asc()  // 按版本升序排序
                .orderByProcessDefinitionKey().asc();     // 按 key 升序排序

        // 定义分页参数
//        int pageNumber = 1;  // 页码，从1开始
//        int pageSize = 10;   // 每页显示的记录数
//        int firstResult = (pageNumber - 1) * pageSize; // 计算起始索引
//        List<ProcessDefinition> processDefinitions = query.listPage(firstResult, pageSize);// 执行分页查询并获取结果


        List<ProcessDefinition> processDefinitions = query.list();
        //使用流和收集器来获取每个 key 的最新版本
        //ps：这种方式就无法实现分页
        Map<String, ProcessDefinition> latestVersions = processDefinitions.stream()
                .collect(Collectors.toMap(
                        ProcessDefinition::getKey, // 分组依据为 processDefinitionKey
                        pd -> pd,                  // 要收集的值为 ProcessDefinition 对象本身
                        (existing, replacement) -> // 如果遇到重复的 key，保留 version 较高的
                                existing.getVersion() >= replacement.getVersion() ? existing : replacement
                ));

        // 将结果转换回列表并返回
        List<ProcessDefinition> newProcessDefinitions = new ArrayList<>(latestVersions.values());


        // 遍历流程定义列表并打印信息
        for (ProcessDefinition processDefinition : newProcessDefinitions) {
            System.out.println("流程ID: " + processDefinition.getId());
            System.out.println("流程名称: " + processDefinition.getName());
            System.out.println("流程标识Key（文件定义）: " + processDefinition.getKey());
            System.out.println("流程版本（同一key的不同版本）: " + processDefinition.getVersion());
            System.out.println("流程部署ID（act_re_deployment）: " + processDefinition.getDeploymentId());
            System.out.println("流程文件名称: " + processDefinition.getResourceName());
            System.out.println("流程图片名称: " + processDefinition.getDiagramResourceName());
            System.out.println("是否挂起（true，则被挂起，不能启动新的流程实例，已有的流程实例可以继续执行） " + processDefinition.isSuspended());
            System.out.println("租户ID: " + processDefinition.getTenantId());
            System.out.println("流程定义的类别: " + processDefinition.getCategory());
            System.out.println("流程描述: " + processDefinition.getDescription());
            // 通过 DeploymentId 查询 Deployment 对象以获取部署时间
            Deployment deployment = repositoryService.createDeploymentQuery()
                    .deploymentId(processDefinition.getDeploymentId())
                    .singleResult();
            System.out.println("流程部署时间: " + deployment.getDeploymentTime());
            System.out.println("----------------------------");
        }
    }

    /**
     * 4.根据流程id获取流程详情
     */
    @RequestMapping("getProcessesById")
    @ResponseBody
    public void getProcessesById() {
        String id = "myProcess_1:10:3435deaa-bedd-11ef-adb5-04ed33b13d42";

        //获取ProcessDefinitionQuery对象，用来查询操作
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        ProcessDefinition processDefinition = processDefinitionQuery
                .processDefinitionId(id)
                .singleResult();
        //输出流程定义的信息
        System.out.println("流程ID: " + processDefinition.getId());
        System.out.println("流程名称: " + processDefinition.getName());
        System.out.println("流程标识Key（文件定义）: " + processDefinition.getKey());
        System.out.println("流程版本（同一key的不同版本）: " + processDefinition.getVersion());
        System.out.println("流程部署ID（act_re_deployment）: " + processDefinition.getDeploymentId());
        System.out.println("流程文件名称: " + processDefinition.getResourceName());
        System.out.println("流程图片名称: " + processDefinition.getDiagramResourceName());
        System.out.println("是否挂起（true，则被挂起，不能启动新的流程实例，已有的流程实例可以继续执行） " + processDefinition.isSuspended());
        System.out.println("租户ID: " + processDefinition.getTenantId());
        System.out.println("流程定义的类别: " + processDefinition.getCategory());
        System.out.println("流程描述: " + processDefinition.getDescription());


        // 通过 DeploymentId 查询 Deployment 对象以获取部署时间
        Deployment deployment = repositoryService.createDeploymentQuery()
                .deploymentId(processDefinition.getDeploymentId())
                .singleResult();
        System.out.println("流程部署时间: " + deployment.getDeploymentTime());
        System.out.println("----------------------------");
    }


    /**
     * 5.导入流程模型xml
     *
     * @param id
     * @param name
     * @param description
     * @param file
     * @return
     */
    @PostMapping({"/processModel/import/{id}"})
    public void importProcessModel(@PathVariable String id, @RequestParam(value = "name") String name,
                                   @RequestParam(value = "description") String description, @RequestParam(value = "file") MultipartFile file) {
        try {
            Model model = this.repositoryService.getModel(id);
            System.out.println("ModelSaveRestResource.saveModel----------");
            ObjectNode modelJson = (ObjectNode) this.objectMapper.readTree(model.getMetaInfo());

            modelJson.put("name", name);
            modelJson.put("description", description);
            model.setMetaInfo(modelJson.toString());
            model.setName(name);
            this.repositoryService.saveModel(model);

            InputStream fileInputStream = file.getInputStream();
            XMLInputFactory xif = XMLInputFactory.newInstance();
            InputStreamReader in = new InputStreamReader(fileInputStream, "UTF-8");
            XMLStreamReader xtr = xif.createXMLStreamReader(in);
            BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(xtr);
            ObjectNode objectNode = new BpmnJsonConverter().convertToJson(bpmnModel);

            this.repositoryService.addModelEditorSource(model.getId(), objectNode.toString().getBytes("utf-8"));


        } catch (Exception e) {
            e.printStackTrace();
        }

    }


    /**
     * 6. 流程配置(获取变量赋值）
     */
    @RequestMapping("getProcessVariable")
    @ResponseBody
    public void getProcessVariable() {
        BpmnModel bpmnModel = repositoryService.getBpmnModel("myProcess_1:10:3435deaa-bedd-11ef-adb5-04ed33b13d42");
        List<Process> processes = bpmnModel.getProcesses();
        Process process = processes.get(0);
        Collection<FlowElement> flowElements = process.getFlowElements();
        for (FlowElement element : flowElements) {
            if (element instanceof StartEvent) {
                System.out.println("流程节点id：" + element.getId());
                System.out.println("流程节点类型：StartEvent开始");
                System.out.println("流程节点名称：" + element.getName());
                System.out.println("--------------------------");
            } else if (element instanceof UserTask) {
                UserTask userTask = (UserTask) element;
                System.out.println("流程节点id：" + userTask.getId());
                System.out.println("流程节点类型：UserTask人员节点");
                System.out.println("流程节点用户变量：" + formatVariable(userTask.getAssignee()));
                if (userTask.getCandidateUsers() != null && userTask.getCandidateUsers().size() > 0) {
                    for (int i = 0; i < userTask.getCandidateUsers().size(); i++) {
                        String user = userTask.getCandidateUsers().get(i);
                        System.out.println("流程节点用户组变量-" + i + "：" + formatVariable(user));
                    }
                }
                if (userTask.getCandidateGroups() != null && userTask.getCandidateGroups().size() > 0) {
                    for (int i = 0; i < userTask.getCandidateGroups().size(); i++) {
                        String group = userTask.getCandidateGroups().get(i);
                        System.out.println("流程节点角色组变量-" + i + "：" + formatVariable(group));
                    }
                }
                System.out.println("流程节点名称：" + userTask.getName());
                System.out.println("--------------------------");
            } else if (element instanceof SequenceFlow) {
                System.out.println("流程节点id：" + element.getId());
                System.out.println("流程节点类型：SequenceFlow分支");
                System.out.println("流程节点顺序：" + ((SequenceFlow) element).getSourceRef() + " -> " + ((SequenceFlow) element).getTargetRef());
                System.out.println("流程节点变量：" + formatExpression(((SequenceFlow) element).getConditionExpression()));
                System.out.println("--------------------------");
            }
        }

        //展示页面效果
        //1.start节点默认发起人为当前用户
        //2.自定义表，选择审批人员类型（角色、部门、用户），统一使用CandidateGroups。
        // ps：实例id，审批人员类型，记录变量名和对应的角色、部门、用户


//        流程节点id：_3
//        流程节点类型：UserTask人员节点
//        流程节点用户变量：assignee1
//        流程节点用户组变量-0：candidateUsers1
//        流程节点用户组变量-1：candidateUsers11
//        流程节点用户组变量-2：candidateUsers111
//        流程节点角色组变量-0：role1
//        流程节点角色组变量-1：role11
//        流程节点角色组变量-2：role111
//        流程节点名称：申请假期
//                --------------------------
//        流程节点id：_4
//        流程节点类型：UserTask人员节点
//        流程节点用户变量：assignee2
//        流程节点用户组变量-0：candidateUsers2
//        流程节点用户组变量-1：candidateUsers22
//        流程节点用户组变量-2：candidateUsers222
//        流程节点角色组变量-0：role2
//        流程节点角色组变量-1：role22
//        流程节点角色组变量-2：role222
//        流程节点名称：经理审批
//                --------------------------
    }


    /**
     * 使用正则表达式解析条件表达式中的变量
     */
    private static String formatVariable(String expression) {
        if (StringUtils.isEmpty(expression)) {
            return null;
        }
        Pattern pattern = Pattern.compile("\\$\\{([^}]*)\\}|#\\{([^}]*)\\}"); // 匹配 ${variable} 或 #{variable}
        Matcher matcher = pattern.matcher(expression);

        String res = null;
        while (matcher.find()) {
            for (int i = 1; i <= matcher.groupCount(); i++) {
                if (matcher.group(i) != null) {
                    res = matcher.group(i).trim();
                }
            }
        }
        return res;
    }

    /**
     * 使用正则表达式解析条件表达式中的变量
     */
    private static String formatExpression(String expression) {
        if (StringUtils.isEmpty(expression)) {
            return null;
        }
        Pattern pattern = Pattern.compile("\\$\\{([^}]*)\\}|#\\{([^}]*)\\}"); // 匹配 ${variable} 或 #{variable}
        Matcher matcher = pattern.matcher(expression);

        String res = null;
        while (matcher.find()) {
            for (int i = 1; i <= matcher.groupCount(); i++) {
                if (matcher.group(i) != null) {
                    res = matcher.group(i).trim();
                }
            }
        }

        // 匹配变量名后跟比较操作符的模式，如 type == 1, type >= 1, type <= 1, type > 1, type < 1, type != 1
        Pattern pattern2 = Pattern.compile("(\\w+\\.?\\w*)\\s*[=<>!]+\\s*[^=<>!]");
        Matcher matcher2 = pattern2.matcher(res);
        while (matcher2.find()) {
            res = matcher2.group(1).trim();
        }
        return res;
    }

}
